/**
 * task.c
 *
 * Copyright (C) 2007 Sadrul Habib Chowdhury <sadrul@pidgin.im>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "task.h"
#include <libxml/parser.h>
#include <string.h>

static GList *tasks;
static GList *tags;
static char *userdir;

#define SAFE(string) (string ? string : "")

static gboolean
save_cb(gpointer data)
{
	GList *iter;
	xmlDoc *doc;
	xmlNode *node, *root;
	char * file = g_build_filename(userdir, "tasks.xml", NULL);

	doc = xmlNewDoc(BAD_CAST "1.0");
	root = xmlNewNode(NULL, BAD_CAST "tasks");
	xmlDocSetRootElement(doc, root);

	for (iter = tasks; iter; iter = iter->next) {
		GTask *task = iter->data;
		char *esc;
		GList *tags;

		node = xmlNewChild(root, NULL, BAD_CAST "task", NULL);
		xmlNewChild(node, NULL, BAD_CAST "name", BAD_CAST(esc = g_markup_escape_text(SAFE(task->name), -1)));
		g_free(esc);
		xmlNewChild(node, NULL, BAD_CAST "details", BAD_CAST(esc = g_markup_escape_text(SAFE(task->details), -1)));
		g_free(esc);

		for (tags = task->tags; tags; tags = tags->next) {
			GTag *tag = tags->data;
			xmlNewChild(node, NULL, BAD_CAST "tag", BAD_CAST(esc = g_markup_escape_text(SAFE(tag->name), -1)));
			g_free(esc);
		}
	}

	xmlSaveFormatFileEnc(file, doc, "UTF-8", 1);
	xmlFreeDoc(doc);
	xmlCleanupParser();

	g_free(file);
	*(int *)data = 0;
	return FALSE;
}

void gt_tasks_save(gboolean immediately)
{
	static int saver = 0;
	if (immediately) {
		save_cb(&saver);
		return;
	}
	if (saver)
		return;
	saver = g_timeout_add(15000, save_cb, &saver);
}

gint gt_task_compare(gconstpointer a, gconstpointer b)
{
	const GTask *t = a, *q = b;
	return g_utf8_collate(t->name, q->name);
}

int gt_tag_compare(gconstpointer a, gconstpointer b)
{
	const GTag *t = a, *q = b;
	return g_utf8_collate(t->name, q->name);
}

static int
find_task(gconstpointer a, gconstpointer b)
{
	const GTask *t = a;
	return g_utf8_collate(t->name, b);
}

static int
find_tag(gconstpointer a, gconstpointer b)
{
	const GTag *t = a;
	return g_utf8_collate(t->name, b);
}

GTask * gt_task_new(const char *name, const char *details)
{
	GTask *task;
	GList *iter;

	iter = g_list_find_custom(tasks, name, find_task);
	if (iter)
		return iter->data;

	task = g_new0(GTask, 1);
	task->name = g_strdup(name);
	task->details = g_strdup(details);
	task->urgency = G_URGENT_NORMAL;

	tasks = g_list_append(tasks, task);

	return task;
}

GTag * gt_tag_new(const char *name)
{
	GTag *tag;
	GList *iter;

	iter = g_list_find_custom(tags, name, find_tag);
	if (iter)
		return iter->data;
	
	tag = g_new0(GTag, 1);
	tag->name = g_strdup(name);

	tags = g_list_append(tags, tag);

	return tag;
}

void gt_task_add_tag(GTask *task, GTag *tag)
{
	task->tags = g_list_append(task->tags, tag);
	tag->tasks = g_list_append(tag->tasks, task);
}

void gt_task_remove_tag(GTask *task, GTag *tag)
{
	task->tags = g_list_remove(task->tags, tag);
	tag->tasks = g_list_remove(tag->tasks, task);
}

gboolean gt_task_is_tagged(GTask *task, const char *tagname)
{
	return (g_list_find_custom(task->tags, tagname, find_tag) != NULL);
}

void gt_task_delete(GTask *task)
{
	while (task->tags)
		gt_task_remove_tag(task, task->tags->data);
	tasks = g_list_remove(tasks, task);
	g_free(task->name);
	g_free(task->details);
	g_free(task);
}

void gt_tasks_read_all(const char *filename)
{
	xmlDocPtr doc;
	xmlNodePtr node;
	const char *name;

	if (!g_file_test(filename, G_FILE_TEST_EXISTS))
		return;

	doc = xmlReadFile(filename, NULL, 0);
	node = xmlDocGetRootElement(doc);

	name = (const char *)node->name;
	while (node && strcmp(name, "tasks"))
		node = node->next;
	for (node = (node ? node->children : NULL); node; node = node->next) {
		char *nm = NULL, *det = NULL;
		GTask *task = NULL;
		xmlNode *child = NULL;

		if (node->type != XML_ELEMENT_NODE)
			continue;

		name = (const char *)node->name;
		if (strcmp(name, "task") != 0)
			continue;

		for (child = node->children; child; child = child->next) {
			if (child->type != XML_ELEMENT_NODE)
				continue;
			name = (const char *)child->name;
			if (strcmp(name, "name") == 0)
				nm = (char *)xmlNodeGetContent(child);
			else if (strcmp(name, "details") == 0)
				det = (char *)xmlNodeGetContent(child);
		}
		if (nm) {
			task = gt_task_new(nm, det);
			xmlFree(nm);
			xmlFree(det);
			for (child = node->children; child; child = child->next) {
				if (child->type != XML_ELEMENT_NODE)
					continue;
				name = (const char *)child->name;
				if (strcmp(name, "tag") == 0) {
					char *tag = (char *)xmlNodeGetContent(child);
					gt_task_add_tag(task, gt_tag_new(tag));
					xmlFree(tag);
				}
			}
		}
	}

	xmlFreeDoc(doc);
	xmlCleanupParser();
}

const GList * gt_tasks_get_all()
{
	return tasks;
}

const GList * gt_tags_get_all()
{
	return tags;
}

void gt_tasks_write_file(const char *filename)
{
	xmlDoc *doc;
	xmlNode *node;

	doc = xmlNewDoc(BAD_CAST "1.0");
	node = xmlNewNode(NULL, BAD_CAST "tasks");
	xmlDocSetRootElement(doc, node);

	xmlSaveFormatFileEnc(filename, doc, "UTF-8", 1);
	xmlFreeDoc(doc);
	xmlCleanupParser();
}

void gt_tasks_set_homedir(const char *homedir)
{
	g_free(userdir);
	userdir = g_strdup(homedir);
	if (!g_file_test(userdir, G_FILE_TEST_IS_DIR)) {
		g_mkdir_with_parents(userdir, 0700);
	}
}

void gt_tasks_load()
{
	char *file;
	file = g_build_filename(userdir, "tasks.xml", NULL);
	gt_tasks_read_all(file);
	g_free(file);
}

void gt_task_add_tags_from_string(GTask *task, const char *text)
{
	char **tags = g_strsplit(text, ",", -1);
	int i;

	for (i = 0; tags[i]; i++) {
		if (!tags[i][0])
			continue;
		gt_task_add_tag(task, gt_tag_new(g_strstrip(tags[i])));
	}
	g_strfreev(tags);
}

